<?php
/**
 *+------------------
 * madong
 *+------------------
 * Copyright (c) https://gitee.com/motion-code  All rights reserved.
 *+------------------
 * Author: Mr. April (405784684@qq.com)
 *+------------------
 * Official Website: http://www.madong.tech
 */

namespace madong\think\wf\dao;

use madong\ingenious\libs\utils\ArrayHelper;
use madong\think\wf\basic\BaseDao;
use madong\think\wf\common\Util;
use madong\think\wf\model\ProcessCcInstance;
use madong\think\wf\model\ProcessInstance;
use madong\think\wf\model\ProcessDefine;
use think\facade\Db;

/**
 * @author Mr.April
 * @since  1.0
 */
class ProcessCcInstanceDao extends BaseDao
{

    protected function setModel(): string
    {
        return ProcessCcInstance::class;
    }

    /**
     * 获取抄送流程列表-待优化think-orm 不支持whereHas 所有只能通过连表方式实现 可以使用模型自定义实现
     *
     * @param $where
     * @param $field
     * @param $page
     * @param $limit
     * @param $order
     * @param $with
     *
     * @return array
     */
    public function getList($where = [], $field = ['*'], $page = 0, $limit = 0, $order = '', $with = []): array
    {
        // 定义前缀
        $prefixes = [
            'pc' => 'pc.',
            'pi' => 'pi.',
            'pd' => 'pd.',
        ];

        // 组装条件
        $where = $this->buildWhere($where, $prefixes);

        // 定义字段
        $fields = $this->defineFields($prefixes);

        // 获取表名
        $tableNames = $this->getTableNames();

        // 获取数据链接
        $connection = Util::getConnectionConfig();

        // 构建查询对象
        $query = $this->buildQuery($tableNames, $fields, $where, $prefixes, $connection);

        //获取总数
        $total = $query->count();

        // 获取数据并格式化
        $data = $query->page($page, $limit)->select()->toArray();
        return [
            $this->formatResults($data),
            $total,
        ];
    }

    private function getTableNames(): array
    {
        return [
            'define'      => ProcessDefine::getTableName(),
            'instance'    => ProcessInstance::getTableName(),
            'instance_cc' => ProcessCcInstance::getTableName(),
        ];
    }

    private function buildQuery($tableNames, $fields, $where, $prefixes, $connection = null)
    {
        return Db::connect($connection)
            ->table($tableNames['instance_cc'])
            ->alias(Util::removeTrailingSubstring($prefixes['pc']))
            ->field($fields)
            ->join([$tableNames['instance'] => Util::removeTrailingSubstring($prefixes['pi'])], 'pc.process_instance_id = pi.id')
            ->join([$tableNames['define'] => Util::removeTrailingSubstring($prefixes['pd'])], 'pi.process_define_id = pd.id')
            ->where($where)
            ->order('pc.create_time', 'desc');
    }

    private function buildWhere(array $where, array $prefixes): array
    {
        $cmap = ArrayHelper::filterArray(ArrayHelper::paramsFilter($where, [
            ['actor_id', '', '', $prefixes['pc'] . 'actor_id'],
            ['state', 1, '', $prefixes['pc'] . 'state'],
            ['process_instance_id', '', '', $prefixes['pc'] . 'process_instance_id'],
        ]));
        $imap = ArrayHelper::filterArray(ArrayHelper::paramsFilter($where, [
            ['business_no', '', '', $prefixes['pi'] . 'business_no'],
            ['operator', '', '', $prefixes['pi'] . 'operator'],
        ]));
        $dmap = ArrayHelper::filterArray(ArrayHelper::paramsFilter($where, [
            ['name', '', '', $prefixes['pd'] . 'name'],
            ['display_name', '', '', $prefixes['pd'] . 'display_name'],
        ]));
        return array_merge($cmap, $imap, $dmap);
    }

    private function defineFields(array $prefixes): string
    {
        $ccInstanceField = Util::prefixFields([
            'id', 'process_instance_id', 'process_task_id', 'actor_id', 'state', 'create_time', 'create_by', 'update_time', 'update_by', 'remark',
        ], $prefixes['pc']);

        $instanceField = Util::prefixFields([
            'id', 'parent_id', 'process_define_id', 'state', 'parent_node_name', 'business_no',
            'operator', 'variable', 'expire_time', 'create_time', 'create_user', 'update_time', 'update_user',
        ], $prefixes['pi']);

        $defineField = Util::prefixFields([
            'id', 'type_id', 'icon', 'name', 'display_name', 'description', 'enabled',
            'is_active', 'content', 'version', 'create_time', 'create_user', 'update_time', 'update_user', 'delete_time',
        ], $prefixes['pd']);

        return Util::mergeFieldStrings([$ccInstanceField, $instanceField, $defineField]);
    }

    private function formatResults(array $data): array
    {
        return array_map(function ($item) {
            return [
                'id'                  => $item['pf_id'],
                'process_instance_id' => $item['pc_process_instance_id'],
                'process_task_id'     => $item['pc_process_task_id'],
                'actor_id'            => $item['pc_actor_id'],
                'state'               => $item['pc_state'],
                'create_time'         => $item['pc_create_time'],
                'create_by'           => $item['pc_create_by'],
                'update_time'         => $item['pc_update_time'],
                'update_by'           => $item['pc_update_by'],
                'update_time'         => $item['pc_update_time'],
                'remark'              => $item['pc_remark'],
                'create_date'         => DateTime::timestampToString($item['pc_create_time']),
                'update_date'         => DateTime::timestampToString($item['pc_update_time']),
                'instance'            => $this->formatInstance($item),
            ];
        }, $data);
    }

    private function formatInstance(array $item): array
    {
        return [
            'id'                => $item['pi_id'],
            'parent_id'         => $item['pi_parent_id'],
            'process_define_id' => $item['pi_process_define_id'],
            'state'             => $item['pi_state'],
            'business_no'       => $item['pi_business_no'],
            'operator'          => $item['pi_operator'],
            'variable'          => Util::convertToArrayOrObject($item['pi_variable'], [], true),
            'form_data'         => Util::getFormData($item['pi_variable']),
            'create_time'       => $item['pi_create_time'],
            'create_user'       => $item['pi_create_user'],
            'update_time'       => $item['pi_update_time'],
            'update_user'       => $item['pi_update_user'],
            'create_date'       => DateTime::timestampToString($item['pi_create_time']),
            'update_date'       => DateTime::timestampToString($item['pi_update_time']),
            'define'            => $this->formatDefine($item),
        ];
    }

    private function formatDefine(array $item): array
    {
        return [
            'id'           => $item['pd_id'],
            'type_id'      => $item['pd_type_id'],
            'icon'         => $item['pd_icon'],
            'name'         => $item['pd_name'],
            'display_name' => $item['pd_display_name'],
            'description'  => $item['pd_description'],
            'enabled'      => $item['pd_enabled'],
            'is_active'    => $item['pd_is_active'],
            'content'      => Util::convertToArrayOrObject($item['pd_content'], [], true),
            'version'      => $item['pd_version'],
            'create_time'  => $item['pd_create_time'],
            'create_user'  => $item['pd_create_user'],
            'update_time'  => $item['pd_update_time'],
            'update_user'  => $item['pd_update_user'],
            'create_date'  => DateTime::timestampToString($item['pd_create_time']),
            'update_date'  => DateTime::timestampToString($item['pd_update_time']),
        ];
    }

}
